package com.cw.frame.codegen.engine;

import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Properties;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.velocity.VelocityContext;
import org.apache.velocity.app.VelocityEngine;

import com.cw.frame.codegen.parameter.ParamBundle;
import com.cw.jeeyt.common.enums.EnumGenerateCodeType;
import com.cw.frame.codegen.Configuration;
import com.cw.frame.codegen.Constant;
import com.cw.frame.codegen.FileHelper;

/**
 * The template engine implementation based on Velocity API.
 *
 * @author chenw
 */
public class VelocityTemplateEngine implements TemplateEngine {
    private Log logger = LogFactory.getLog(getClass());

    private VelocityEngine velocityEngine;

    @Override
    public void process(ParamBundle paramBundle, String templateName, String targetFilePath) throws Exception {
        if (logger.isDebugEnabled()) {
            logger.debug(
                "To process paramBundle: " + paramBundle + " , templateName: " + templateName + " , targetFilePath: "
                    + targetFilePath);
        }
        String generateType = paramBundle.getGenerateType();
        VelocityEngine engine = getVelocityEngine(generateType);
        if (engine == null) {
            logger.error("Failed to create VelocityEngine !");
            return;
        }

        OutputStreamWriter outputStreamWriter = createOutputStreamWriter(targetFilePath);
        if (outputStreamWriter == null) {
            return;
        }

        VelocityContext context = new VelocityContext();
        context.put("params", paramBundle);
        context.put("classNamePrefix", FileHelper.getJavaFilePrefix(paramBundle.getModuleName()));
        context.put("createDate", new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date()));
        context.put("author", paramBundle.getAuthor());
        context.put("pagePath", paramBundle.getPagePath());
        context.put("xmlPath", paramBundle.getXmlPath());

        try {
            engine.mergeTemplate(templateName, Constant.TEMPLATE_FILE_ENCODING, context, outputStreamWriter);

            outputStreamWriter.flush();

        } catch (Exception e) {
            logger.error("Error when merge template", e);
            throw new Exception(e.getMessage());
        } finally {
            try {
                outputStreamWriter.close();
            } catch (Exception e) {
                logger.error("Error when close  outputStreamWriter", e);
                throw new Exception(e.getMessage());
            }
        }

        if (logger.isInfoEnabled()) {
            logger.info("Succeed to create source file from template: " + templateName);
        }

    }

    private OutputStreamWriter createOutputStreamWriter(String targetFilePath) throws Exception {
        OutputStream out = null;
        try {
            FileHelper.createFileIfNotExist(targetFilePath);
            out = new FileOutputStream(targetFilePath);
        } catch (FileNotFoundException e) {
            logger.error("Can't find file: " + targetFilePath, e);
            throw new Exception(e.getMessage());
        }

        OutputStreamWriter outputStreamWriter = null;
        try {
            outputStreamWriter = new OutputStreamWriter(out, Constant.TEMPLATE_FILE_ENCODING);
        } catch (Exception e) {
            logger.error("Error when create  outputStreamWriter", e);
            throw new Exception(e.getMessage());
        }
        return outputStreamWriter;
    }

    private VelocityEngine getVelocityEngine(String generateType) throws Exception {
        synchronized (this) {
            if (velocityEngine != null) {
                return velocityEngine;
            }

            Properties properties = new Properties();
            try {
                properties.load(this.getClass()
                    .getResourceAsStream("/velocity.properties"));
            } catch (IOException e) {
                logger.error("can't load configuration from velocity.properties", e);
                return null;
            }

            // Override the resource path configuration
            setCustomizedOptions(properties, generateType);

            velocityEngine = new VelocityEngine();
            velocityEngine.init(properties);

            if (logger.isInfoEnabled()) {
                logger.info("Succeed to init VelocityEngine instance: " + velocityEngine);
            }
        }

        return velocityEngine;
    }

    private void setCustomizedOptions(Properties properties, String generateType) throws Exception {

        Configuration configuration = new Configuration();
        if (EnumGenerateCodeType.PAGE.getCode()
            .equals(generateType)) {
            configuration = Configuration.getPageConfig();
        } else if (EnumGenerateCodeType.CLASS.getCode()
            .equals(generateType)) {
            configuration = Configuration.getClassConfig();
        } else if (EnumGenerateCodeType.JDBC.getCode()
            .equals(generateType)) {
            configuration = Configuration.getJdbcConfig();
        } else if (EnumGenerateCodeType.PROPERTIES.getCode()
            .equals(generateType)) {
            configuration = Configuration.getDefault();
        } else {
            throw new Exception("不能识别当前生成代码方式");
        }

        String resourcePath = configuration.getJavaTemplatePath() + "," + configuration.getPageTemplatePath() + ","
            + configuration.getXmlTemplatePath();
        properties.put("file.resource.loader.path", resourcePath);
    }

}
